using System;
using System.ComponentModel;

namespace Microsoft.WowAddonStudio.FrameXml.Components.Descriptors
{
    public class FrameXmlPropertyDescriptor<T> : PropertyDescriptor where T : class
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="FrameXmlPropertyDescriptor{T}"/> class.
        /// </summary>
        /// <param name="instance">The inheritable object.</param>
        /// <param name="propertyDescriptor">The property descriptor.</param>
        /// <param name="propertyName">Name of the property.</param>
        public FrameXmlPropertyDescriptor(T instance, PropertyDescriptor propertyDescriptor, string propertyName)
            : base(propertyDescriptor)
        {
            if (propertyDescriptor == null)
                throw new ArgumentNullException("propertyDescriptor");

            this.Instance = instance;
            this.PropertyDescriptor = propertyDescriptor;
            this.PropertyName = propertyName;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FrameXmlPropertyDescriptor{T}"/> class.
        /// </summary>
        /// <param name="instance">The inheritable object.</param>
        /// <param name="propertyDescriptor">The property descriptor.</param>
        public FrameXmlPropertyDescriptor(T instance, PropertyDescriptor propertyDescriptor)
            : this(instance, propertyDescriptor, propertyDescriptor.ComponentType.Name)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FrameXmlPropertyDescriptor{T}"/> class.
        /// </summary>
        /// <param name="propertyDescriptor">The property descriptor.</param>
        public FrameXmlPropertyDescriptor(PropertyDescriptor propertyDescriptor)
            : this(null, propertyDescriptor, propertyDescriptor.ComponentType.Name)
        {
        }

        /// <summary>
        /// Gets or sets the instance.
        /// </summary>
        /// <value>The instance.</value>
        protected T Instance { get; private set; }

        /// <summary>
        /// Gets or sets the property descriptor.
        /// </summary>
        /// <value>The property descriptor.</value>
        protected PropertyDescriptor PropertyDescriptor { get; private set; }

        /// <summary>
        /// Gets or sets the name of the property.
        /// </summary>
        /// <value>The name of the property.</value>
        protected string PropertyName { get; private set; }

        /// <summary>
        /// When overridden in a derived class, gets the type of the component this property is bound to.
        /// </summary>
        /// <value></value>
        /// <returns>A <see cref="T:System.Type"/> that represents the type of component this property is bound to. When the <see cref="M:System.ComponentModel.PropertyDescriptor.GetValue(System.Object)"/> or <see cref="M:System.ComponentModel.PropertyDescriptor.SetValue(System.Object,System.Object)"/> methods are invoked, the object specified might be an instance of this type.</returns>
        public override Type ComponentType
        {
            get { return PropertyDescriptor.ComponentType; }
        }

        /// <summary>
        /// When overridden in a derived class, gets a value indicating whether this property is read-only.
        /// </summary>
        /// <value></value>
        /// <returns>true if the property is read-only; otherwise, false.</returns>
        public override bool IsReadOnly
        {
            get { return PropertyDescriptor.IsReadOnly; }
        }

        /// <summary>
        /// When overridden in a derived class, gets the type of the property.
        /// </summary>
        /// <value></value>
        /// <returns>A <see cref="T:System.Type"/> that represents the type of the property.</returns>
        public override Type PropertyType
        {
            get { return PropertyDescriptor.PropertyType; }
        }

        /// <summary>
        /// When overridden in a derived class, returns whether resetting an object changes its value.
        /// </summary>
        /// <param name="component">The component to test for reset capability.</param>
        /// <returns>
        /// true if resetting the component changes its value; otherwise, false.
        /// </returns>
        public override bool CanResetValue(object component)
        {
            return PropertyDescriptor.CanResetValue(component);
        }

        /// <summary>
        /// When overridden in a derived class, gets the current value of the property on a component.
        /// </summary>
        /// <param name="component">The component with the property for which to retrieve the value.</param>
        /// <returns>
        /// The value of a property for a given component.
        /// </returns>
        public override object GetValue(object component)
        {
            return PropertyDescriptor.GetValue(component);
        }

        /// <summary>
        /// When overridden in a derived class, resets the value for this property of the component to the default value.
        /// </summary>
        /// <param name="component">The component with the property value that is to be reset to the default value.</param>
        public override void ResetValue(object component)
        {
            PropertyDescriptor.ResetValue(component);
        }

        /// <summary>
        /// When overridden in a derived class, sets the value of the component to a different value.
        /// </summary>
        /// <param name="component">The component with the property value that is to be set.</param>
        /// <param name="value">The new value.</param>
        public override void SetValue(object component, object value)
        {
            PropertyDescriptor.SetValue(component, value);
        }

        /// <summary>
        /// When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted.
        /// </summary>
        /// <param name="component">The component with the property to be examined for persistence.</param>
        /// <returns>
        /// true if the property should be persisted; otherwise, false.
        /// </returns>
        public override bool ShouldSerializeValue(object component)
        {
            return PropertyDescriptor.ShouldSerializeValue(component);
        }
    }
}